package net.osc.service.query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.osc.model.query.QuerySegment;
import net.osc.model.query.QueryUnit;
import net.osc.service.BaseManager;
import net.osc.util.PageBean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import bsh.EvalError;
import bsh.Interpreter;

@Service("commonQueryManager")
@Transactional(readOnly = true)
public class CommonQueryManagerImpl extends BaseManager implements
		CommonQueryManager {
	private static final Log logger = LogFactory
			.getLog(CommonQueryManagerImpl.class);

	private Pattern paramPtn = Pattern
			.compile("\\$\\{(.*?)\\}", Pattern.DOTALL);
	private Pattern namedParamPtn = Pattern.compile(":(\\w+)", Pattern.DOTALL);

	@Transactional
	public void saveQueryUnit(QueryUnit queryUnit) {
		hibernateDao.save(queryUnit);
	}

	@Transactional
	public void saveQuerySegment(QuerySegment querySegment) {
		hibernateDao.save(querySegment);
		if (querySegment.getSegmentOrder() == null) {
			Number maxOrder = (Number) hibernateDao
					.findUniqueResult(
							"select max(segmentOrder) from QuerySegment where queryUnit = :queryUnit",
							new String[] { "queryUnit" },
							new Object[] { querySegment.getQueryUnit() });
			maxOrder = maxOrder == null ? -1 : maxOrder;
			querySegment.setSegmentOrder(maxOrder.intValue() + 1);
			hibernateDao.save(querySegment);
		} else {
			List<QuerySegment> others = hibernateDao.find(
					"from QuerySegment qs where qs.queryUnit = ? and qs != ?",
					querySegment.getQueryUnit(), querySegment);
			Collections.sort(others);
			for (int i = 0; i < others.size(); i++) {
				QuerySegment other = others.get(i);
				if (i < querySegment.getSegmentOrder()) {
					other.setSegmentOrder(i);
				} else {
					int j = i - querySegment.getSegmentOrder();
					other.setSegmentOrder(querySegment.getSegmentOrder() + 1
							+ j);
				}
				hibernateDao.save(other);
			}
		}
	}

	@Transactional
	public void delQueryUnits(Long[] ids) {
		for (Long id : ids) {
			hibernateDao.bulkUpdate(
					"delete from QuerySegment where queryUnit.id = ?", id);
			QueryUnit qu = hibernateDao.get(QueryUnit.class, id);
			hibernateDao.delete(qu);
		}
	}

	@Transactional
	public void delQuerySegments(Long[] ids) {
		for (Long id : ids) {
			QuerySegment querySegment = hibernateDao
					.get(QuerySegment.class, id);
			hibernateDao.delete(querySegment);
		}
	}

	public QueryUnit getQuery(String key) {
		QueryUnit queryUnit = hibernateDao.findUniqueResult(
				"from QueryUnit qu where qu.key = ?", key);
		if (queryUnit == null) {
			throw new RuntimeException("can not found mapped query by key : "
					+ key);
		}
		return queryUnit;
	}

	public <T> List<T> doQuery(String key, Map<String, Object> paramMap) {
		return doQuery(getQuery(key), paramMap);
	}

	public <T> List<T> doQuery(QueryUnit queryUnit, Map<String, Object> paramMap) {
		Interpreter bsh = getBsh(paramMap);
		String queryText = getQueryUnitText(queryUnit, bsh);
		List<String> paramNames = new ArrayList<String>();
		List<Object> params = new ArrayList<Object>();
		List<String> listParamNames = new ArrayList<String>();
		List<Collection<?>> listParams = new ArrayList<Collection<?>>();
		collectNamedParams(queryText, bsh, paramNames, params, listParamNames,
				listParams);
		if (QueryUnit.QUERY_TYPE_SQL.equalsIgnoreCase(queryUnit.getType())) {
			return hibernateDao.findBySQL(queryText,
					paramNames.toArray(new String[] {}), params.toArray(),
					listParamNames.toArray(new String[] {}),
					listParams.toArray(new Collection<?>[] {}));
		} else {
			return hibernateDao.find(queryText,
					paramNames.toArray(new String[] {}), params.toArray(),
					listParamNames.toArray(new String[] {}),
					listParams.toArray(new Collection<?>[] {}));
		}
	}

	public <T> PageBean<T> doPageQuery(String key,
			Map<String, Object> paramMap, PageBean<T> pageBean) {
		return doPageQuery(getQuery(key), paramMap, pageBean);
	}

	public <T> PageBean<T> doPageQuery(QueryUnit queryUnit,
			Map<String, Object> paramMap, PageBean<T> pageBean) {
		Interpreter bsh = getBsh(paramMap);
		String queryText = getQueryUnitText(queryUnit, bsh);
		List<String> paramNames = new ArrayList<String>();
		List<Object> params = new ArrayList<Object>();
		List<String> listParamNames = new ArrayList<String>();
		List<Collection<?>> listParams = new ArrayList<Collection<?>>();
		collectNamedParams(queryText, bsh, paramNames, params, listParamNames,
				listParams);
		if (QueryUnit.QUERY_TYPE_SQL.equalsIgnoreCase(queryUnit.getType())) {
			return hibernateDao.findBySQLPage(pageBean, queryText,
					paramNames.toArray(new String[] {}), params.toArray(),
					listParamNames.toArray(new String[] {}),
					listParams.toArray(new Collection<?>[] {}));
		} else {
			return hibernateDao.findByPage(pageBean, queryText,
					paramNames.toArray(new String[] {}), params.toArray(),
					listParamNames.toArray(new String[] {}),
					listParams.toArray(new Collection<?>[] {}));
		}
	}

	/**
	 * 根据查询参数返回一个查询单元的查询语句
	 * 
	 * @param queryUnit
	 * @param bsh
	 * @return 返回的查询语句中参数（${xx}）被替换成实际值，但命名参数（:xx）不动
	 */
	private String getQueryUnitText(QueryUnit queryUnit, Interpreter bsh) {
		StringBuffer qlBuf = new StringBuffer(queryUnit.getText());
		for (QuerySegment querySegment : queryUnit.getQuerySegments()) {
			qlBuf.append(getQuerySegmentText(bsh, querySegment));
		}
		String text = qlBuf.toString().trim();
		Matcher m = paramPtn.matcher(text);
		while (m.find()) {
			String paramName = m.group(1);
			try {
				text = text.replace(m.group(),
						String.valueOf(bsh.get(paramName)));
			} catch (EvalError e) {
				logger.warn("failed get from bsh by key[" + paramName + "]");
			}
			m = paramPtn.matcher(text);
		}
		return text;
	}

	/**
	 * 根据条件获取一个动态查询段的查询语句
	 * 
	 * @param bsh
	 * @param querySegment
	 * @return 如果条件不满足，将返回一个空串；条件满足或没有配置条件表达式直接返回动态段语句
	 */
	private String getQuerySegmentText(Interpreter bsh,
			QuerySegment querySegment) {
		StringBuffer textBuf = new StringBuffer();
		String condition = querySegment.getCondition();
		if (condition == null || condition.trim().equals("")) {// 对于没有配置表达式的,认为满足条件
			textBuf.append(querySegment.getText());
		} else {
			try {
				Object obj = bsh.eval(condition);
				if (obj instanceof Boolean) {
					Boolean bool = (Boolean) obj;
					if (bool) {
						textBuf.append(querySegment.getText());
					}
				} else {
					logger.warn("eval condition return not boolean value ["
							+ condition + "]");
				}
			} catch (EvalError e) {
				logger.warn("failed eval condition [" + condition + "]", e);
			}
		}
		if (textBuf.length() > 0) {
			textBuf.insert(0, " ");
		}
		return textBuf.toString();
	}

	/**
	 * 从beanshell收集命名查询参数
	 * 
	 * @param queryText
	 *            查询语句
	 * @param bsh
	 *            带查询参数的beanshell
	 * @param paramNames
	 *            存放收集到的参数名
	 * @param params
	 *            存放收集到的参数
	 * @param listParamNames
	 *            存放收集到的集合参数名
	 * @param listParams
	 *            存放收集到的集合参数
	 */
	private void collectNamedParams(String queryText, Interpreter bsh,
			List<String> paramNames, List<Object> params,
			List<String> listParamNames, List<Collection<?>> listParams) {
		Matcher m = namedParamPtn.matcher(queryText);
		while (m.find()) {
			String paramName = m.group(1);
			try {
				Object param = bsh.get(paramName);
				if (param instanceof Collection) {
					Collection<?> col = (Collection<?>) param;
					listParamNames.add(paramName);
					listParams.add(col);
				} else if (param != null) {
					paramNames.add(paramName);
					params.add(param);
				}
			} catch (EvalError e) {
				logger.warn("failed get from bsh by key[" + paramName + "]");
			}
		}
	}

	/**
	 * 返回带查询参数的beanshell
	 * 
	 * @param paramMap
	 * @return
	 */
	private Interpreter getBsh(Map<String, Object> paramMap) {
		Interpreter bsh = new Interpreter();
		if (paramMap != null) {
			for (String key : paramMap.keySet()) {
				try {
					Object obj = paramMap.get(key);
					if (obj instanceof String) {
						String str = (String) obj;
						if (!str.trim().equals("")) {
							bsh.set(key, str);
						}
					} else if (obj instanceof Collection) {
						Collection<?> col = (Collection<?>) obj;
						if (col.size() > 0) {
							bsh.set(key, col);
						}
					} else if (obj != null) {
						bsh.set(key, obj);
					}
				} catch (EvalError e) {
					logger.warn("failed set to bsh by key[" + key + "]");
				}
			}
		}
		return bsh;
	}

}
